package com.mapforge.util;

/**
 * ColorUtil is a collection of convenience functions concerned with
 * manipulating color data in the default ARGB color model on a bit level. This
 * allows developers to avoid using the color class for manipulation, lessening
 * the load on the garbage collector. Last edited 12/14/09
 * 
 * @author William Morrison
 * 
 */
public final class ColorUtil {
	/**
	 * Accepts an integer in ARGB color model and splits it into alpha, red,
	 * green, and blue components and stores the result in a specified array. If
	 * the specified array is null, a new array is allocated.
	 * 
	 * @param pixel
	 *            specified integer
	 * @param rgbArray
	 *            specified array to place data in, if null a new array is
	 *            allocated and returned
	 * @return array of 4 ints containing alpha, red, green, and blue components
	 *         from the specified pixel.
	 */
	public static int[] parseColor(int pixel, int[] rgbArray) {
		if (rgbArray == null) {
			rgbArray = new int[4];
		} else {
			new ArrayIndexOutOfBoundsException(
					"Improper length to hold RGB or RGBA values, length must be 3 or 4");
		}
		rgbArray[0] = getRed(pixel);
		rgbArray[1] = getGreen(pixel);
		rgbArray[2] = getBlue(pixel);
		rgbArray[3] = getAlpha(pixel);
		return rgbArray;
	}

	/**
	 * Returns a string containing the binary representation of an integer.
	 * 
	 * @param pixel
	 *            pixel to convert to binary string
	 * @return string representation of an integer in binary
	 */
	public static String getBinaryString(int pixel) {
		StringBuffer s = new StringBuffer();
		for (int i = 0; i < Integer.SIZE; i++) {
			s.append((pixel >> i) & 1);
		}
		return s.reverse().toString();
	}

	/**
	 * Returns the red component of the specified integer. Integer must be in
	 * ARGB color model.
	 * 
	 * @param pixel
	 *            specified pixel in ARGB color model
	 * @return the red component of the specified ARGB color (2nd set of 8 bits)
	 */
	public static int getRed(int pixel) {
		return (pixel & 0xFF0000) >> 16;
	}

	/**
	 * Sets the red color component of an integer in ARGB color model and
	 * returns the result.
	 * 
	 * @param pixel
	 *            pixel in ARGB color model
	 * @param red
	 *            new red component
	 * @return pixel in ARGB color model with specified red component
	 */
	public static int setRed(int pixel, byte red) {
		return (pixel & 0xFF00FFFF) | red;
	}

	/**
	 * Returns the green component of the specified integer. Integer must be in
	 * ARGB color model.
	 * 
	 * @param pixel
	 *            integer in ARGB color model
	 * @return green component of integer color
	 */
	public static int getGreen(int pixel) {
		return (pixel & 0xFF00) >> 8;
	}

	/**
	 * Sets the green color component of the specified integer to the specified
	 * new green value. Integer must be in ARGB color model.
	 * 
	 * @param pixel
	 *            integer in ARGB color model.
	 * @param green
	 *            specified new green component to set
	 * @return integer in ARGB color model with the new green component value
	 */
	public static int setGreen(int pixel, byte green) {
		return (pixel & 0xFFFF00FF) | green;
	}

	/**
	 * Returns the blue color component in a specified integer in ARGB color
	 * model.
	 * 
	 * @param pixel
	 *            specified integer
	 * @return blue color component
	 */
	public static int getBlue(int pixel) {
		return pixel & 0xFF;
	}

	/**
	 * Sets the blue color component of the specified integer to the specified
	 * new blue value. Integer must be in ARGB color model.
	 * 
	 * @param pixel
	 *            integer in ARGB color model.
	 * @param blue
	 *            specified new blue component to set
	 * @return integer in ARGB color model with the new blue component value
	 */
	public static int setBlue(int pixel, byte blue) {
		return (pixel & 0xFFFFFF00) | blue;
	}

	/**
	 * Returns the alpha value of the specified integer. Integer must be in ARGB
	 * color model.
	 * 
	 * @param pixel
	 *            specified integer in ARGB color model
	 * @return alpha component of integer color
	 */
	public static int getAlpha(int pixel) {
		return Integer.rotateRight(pixel & 0xFF000000, 24);
	}

	/**
	 * Sets the alpha value of the specified integer to the new alpha value.
	 * 
	 * @param pixel
	 *            integer in ARGB color model
	 * @param alpha
	 *            new alpha value
	 * @return integer in ARGB color model with the new specified alpha value
	 */
	public static int setAlpha(int pixel, byte alpha) {
		return (pixel & 0x00FFFFFF) | (alpha << 24);
	}

	/**
	 * Creates an integer with bits set properly to represent a pixel in the
	 * ARGB color model.
	 * 
	 * @param r
	 * @param g
	 * @param b
	 * @param alpha
	 * @return
	 */
	public static int createColor(int r, int g, int b, int alpha) {
		int pixel = 0;
		pixel |= (alpha << 24) & 0xFF000000;
		pixel |= (r << 16) & 0xFF0000;
		pixel |= (g << 8) & 0x00FF00;
		pixel |= b;
		return pixel;
	}

	/**
	 * Returns the intensity of a color in ARGB color model by calculating the
	 * average intensity of the specified color.
	 * 
	 * Does not consider alpha value.
	 * 
	 * @param argbColor
	 *            specified ARGB color model integer
	 * @return average intensity of color
	 */
	public static int getRGBIntensity(int argbColor) {
		return (getRed(argbColor) + getGreen(argbColor) + getBlue(argbColor)) / 3;
	}

	/**
	 * Returns the maximum of the red green and blue components in an integer in
	 * ARGB color mo. Useful in quickly calculating an approximate grayscale
	 * equivalent of a color. This is faster than calculating the average
	 * intensity because division and multiplication aren't involved but is less
	 * accurate.
	 * 
	 * Does not consider the alpha value.
	 * 
	 * @param argbColor
	 *            specified ARGB color model integer
	 * @return maximum color component in the specified ARGB color model color
	 */
	public static int getRGBMax(int argbColor) {
		return Math.max(Math.max(getRed(argbColor), getGreen(argbColor)),
				getBlue(argbColor));
	}

	/**
	 * Returns the HSV intensity (the average of color components) using an
	 * integer in ARGB color model. Useful in calculating a good approximation
	 * of a colors grayscale equivalent.
	 * 
	 * Does not consider the alpha value.
	 * 
	 * @param argbColor
	 *            specified ARGB color model integer
	 * @return HSV average intensity (see function description)
	 */
	public static int getHSVIntensity(int argbColor) {
		return (getRed(argbColor) + getGreen(argbColor) + getBlue(argbColor)) / 3;
	}

	/**
	 * Returns the YIQ intensity of a pixel in ARGB color model. The YIQ
	 * intensity is the weighted average of the red green and blue components in
	 * a color. Green is weighted most heavily, then red, then blue. This is
	 * because our eyes pick up on green more easily than red, and red more
	 * easily than blue. This is the slowest way to calculate intensity but
	 * considered the most accurate.
	 * 
	 * @param argbColor
	 *            specified integer in ARGB color model
	 * @return YIQ intensity of pixel (see function description)
	 */
	public static int getYIQIntensity(int argbColor) {
		return (int) ((getGreen(argbColor) + getRed(argbColor) / 2 + getBlue(argbColor) / 4) / 445.0 * 255);
	}
}